package com.dongbao.util;

import cn.hutool.core.thread.ExecutorBuilder;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>扩展线程工具类，</p>
 *
 * @version 1.0
 * @author:liuxian
 * @date: 2021-04-24
 * @history
 */
public class DongBaoThreadUtil {

    /**
     * 默认线程系数
     */
    private static final float DEFAULT_BLOCKING_COEFFICIENT = 0.2f;

    /**
     * 初始化线程系数
     */
    private static final float INIT_BLOCKING_COEFFICIENT = 0.9f;

    /**
     * 考勤线程系数
     */
    private static final float ATT_BLOCKING_COEFFICIENT = 0.6f;

    /**
     * 定时任务线程系数
     */
    private static final float CRON_JOB_BLOCKING_COEFFICIENT = 0.5f;

    /**
     * 公共
     */
    public static final String COMMON = "common";

    /**
     * 初始化
     */
    public static final String INIT = "init";

    /**
     * 薪酬
     */
    public static final String SALARY = "salary";

    /**
     * 时间
     */
    public static final String ATT = "att";

    /**
     * 第三方接口
     */
    public static final String THIRD_API = "third_api";

    /**
     * 导出
     */
    public static final String EXPORT = "export";

    /**
     * 硬件
     */
    public static final String HARDWARE = "hardware";

    /**
     * 定时任务
     */
    public static final String CRON_JOB = "cron_job";

    /**
     * 事件
     */
    public static final String EVENT = "event";
    /**
     * 定时任务
     */
    public static final String Report = "report";


    private static final ConcurrentHashMap<String, ThreadPoolExecutor> threadPoolExecutorMap = new ConcurrentHashMap<>();

    private static DongBaoThreadUtil cache = null;

    private DongBaoThreadUtil() {
        put(COMMON, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, COMMON));
        put(INIT, newExecutorByBlockingCoefficient(INIT_BLOCKING_COEFFICIENT, INIT));
        put(ATT, newExecutorByBlockingCoefficient(ATT_BLOCKING_COEFFICIENT, ATT));
        put(THIRD_API, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, THIRD_API));
        put(EXPORT, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, EXPORT));
        put(HARDWARE, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, HARDWARE));
        put(CRON_JOB, newExecutorByBlockingCoefficient(CRON_JOB_BLOCKING_COEFFICIENT, CRON_JOB));
        put(EVENT, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, EVENT));
        put(Report, newExecutorByBlockingCoefficient(DEFAULT_BLOCKING_COEFFICIENT, Report));
    }

    /**
     * 获取该类实例
     */
    public synchronized static DongBaoThreadUtil getInstance() {
        if (cache == null) {
            cache = new DongBaoThreadUtil();
        }
        return cache;
    }

    /**
     * 获取第三方线程池
     */
    public ThreadPoolExecutor getThirdApi() {
        return get(THIRD_API);
    }

    /**
     * 获取薪酬线程池
     */
    public ThreadPoolExecutor getSalary(float blockingCoefficient) {
        return get(SALARY, blockingCoefficient);
    }

    /**
     * 获取时间管理线程池
     */
    public ThreadPoolExecutor getAtt() {
        return get(ATT);
    }

    /**
     * 获取公共线程池
     */
    public ThreadPoolExecutor getCommon() {
        return get(COMMON);
    }

    /**
     * 获取初始化线程池
     */
    public ThreadPoolExecutor getInit() {
        return get(INIT);
    }

    /**
     * 获取导入导出线程池
     */
    public ThreadPoolExecutor getExport() {
        return get(EXPORT);
    }

    /**
     * 获取硬件线程池
     */
    public ThreadPoolExecutor getHardware() {
        return get(HARDWARE);
    }

    /**
     * 获取定时任务线程池
     */
    public ThreadPoolExecutor getCronJob() {
        return get(CRON_JOB);
    }

    /**
     * 获取事件线程池
     */
    public ThreadPoolExecutor getEvent() {
        return get(EVENT);
    }


    /**
     * 报表线程
     * @return
     */
    public ThreadPoolExecutor getReport() {
        return get(Report);
    }

    private ThreadPoolExecutor get(String name) {
        return threadPoolExecutorMap.get(name);
    }

    /**
     * 根据指定的键值获取对应的val
     *
     * @param name 指定的键值
     * @return 返回该key对应的线程
     */
    public synchronized ThreadPoolExecutor get(String name, float blockingCoefficient) {
        ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(name);
        if(null == threadPoolExecutor) {
            threadPoolExecutor = newExecutorByBlockingCoefficient(blockingCoefficient, name);
            put(name, threadPoolExecutor);
        }
        return threadPoolExecutor;
    }

    /**
     * 将key映射到线程加入缓存对象中
     *
     * @param key 名称
     * @param threadPoolExecutor 线程
     */
    public void put(String key, ThreadPoolExecutor threadPoolExecutor) {
        if (threadPoolExecutorMap.containsKey(key)) {
            return;
        }
        threadPoolExecutorMap.put(key, threadPoolExecutor);
    }

    /**
     * 根据线程系数，构建线程池，核心数与最大线程数相同
     * <p>
     * 线程池大小 = CPU核心数/(1-blockingCoefficient)
     * blockingCoefficient 必需在 [0，1) 区间，
     * 所以系数越大线程数越多;
     * </p>
     *
     * @param blockingCoefficient 线程数系数
     * @param name                线程池名称
     * @return java.util.concurrent.ThreadPoolExecutor
     */
    private ThreadPoolExecutor newExecutorByBlockingCoefficient(float blockingCoefficient, String name) {
        if (!(blockingCoefficient >= 1.0F) && !(blockingCoefficient < 0.0F)) {
            int poolSize = (int) ((float) Runtime.getRuntime().availableProcessors() / (1.0F - blockingCoefficient));
            ThreadPoolExecutor rst = ExecutorBuilder.create().setCorePoolSize(poolSize).setMaxPoolSize(poolSize).setKeepAliveTime(300L, TimeUnit.SECONDS).setThreadFactory(new NamedThreadFactory(name)).build();
            rst.allowCoreThreadTimeOut(true);
            return  rst;
        } else {
            throw new IllegalArgumentException("[blockingCoefficient] must between 0 and 1, or equals 0.");
        }
    }


    public static class NamedThreadFactory implements ThreadFactory {
        private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        NamedThreadFactory(String name) {

            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            if (null == name || name.isEmpty()) {
                name = "pool";
            }

            namePrefix = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon()) {
                t.setDaemon(false);
            }
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }
}